home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkMenuDraw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  27.7 KB  |  1,017 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkMenuDraw.c --
  3.  *
  4.  *    This module implements the platform-independent drawing and
  5.  *    geometry calculations of menu widgets.
  6.  *
  7.  * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkMenuDraw.c 1.45 97/07/31 09:09:55
  13.  */
  14.  
  15. #include "tkMenu.h"
  16.  
  17. /*
  18.  * Forward declarations for procedures defined later in this file:
  19.  */
  20.  
  21. static void        AdjustMenuCoords _ANSI_ARGS_ ((TkMenu *menuPtr,
  22.                 TkMenuEntry *mePtr, int *xPtr, int *yPtr,
  23.                 char *string));
  24. static void        ComputeMenuGeometry _ANSI_ARGS_((
  25.                 ClientData clientData));
  26. static void        DisplayMenu _ANSI_ARGS_((ClientData clientData));
  27.  
  28. /*
  29.  *----------------------------------------------------------------------
  30.  *
  31.  * TkMenuInitializeDrawingFields --
  32.  *
  33.  *    Fills in drawing fields of a new menu. Called when new menu is
  34.  *    created by Tk_MenuCmd.
  35.  *
  36.  * Results:
  37.  *    None.
  38.  *
  39.  * Side effects:
  40.  *    menuPtr fields are initialized.
  41.  *
  42.  *----------------------------------------------------------------------
  43.  */
  44.  
  45. void
  46. TkMenuInitializeDrawingFields(menuPtr)
  47.     TkMenu *menuPtr;        /* The menu we are initializing. */
  48. {
  49.     menuPtr->textGC = None;
  50.     menuPtr->gray = None;
  51.     menuPtr->disabledGC = None;
  52.     menuPtr->activeGC = None;
  53.     menuPtr->indicatorGC = None;
  54.     menuPtr->disabledImageGC = None;
  55.     menuPtr->totalWidth = menuPtr->totalHeight = 0;
  56. }
  57.  
  58. /*
  59.  *----------------------------------------------------------------------
  60.  *
  61.  * TkMenuInitializeEntryDrawingFields --
  62.  *
  63.  *    Fills in drawing fields of a new menu entry. Called when an
  64.  *    entry is created.
  65.  *
  66.  * Results:
  67.  *    None.
  68.  *
  69.  * Side effects:
  70.  *    None.
  71.  *
  72.  *----------------------------------------------------------------------
  73.  */
  74.  
  75. void
  76. TkMenuInitializeEntryDrawingFields(mePtr)
  77.     TkMenuEntry *mePtr;        /* The menu we are initializing. */
  78. {
  79.     mePtr->width = 0;
  80.     mePtr->height = 0;
  81.     mePtr->x = 0;
  82.     mePtr->y = 0;
  83.     mePtr->indicatorSpace = 0;
  84.     mePtr->labelWidth = 0;
  85.     mePtr->textGC = None;
  86.     mePtr->activeGC = None;
  87.     mePtr->disabledGC = None;
  88.     mePtr->indicatorGC = None;
  89. }
  90.  
  91. /*
  92.  *----------------------------------------------------------------------
  93.  *
  94.  * TkMenuFreeDrawOptions --
  95.  *
  96.  *    Frees up any structures allocated for the drawing of a menu.
  97.  *    Called when menu is deleted.
  98.  *
  99.  * Results:
  100.  *    None.
  101.  *
  102.  * Side effects:
  103.  *    Storage is released.
  104.  *
  105.  *----------------------------------------------------------------------
  106.  */
  107.  
  108. void
  109. TkMenuFreeDrawOptions(menuPtr)
  110.     TkMenu *menuPtr;
  111. {
  112.     if (menuPtr->textGC != None) {
  113.     Tk_FreeGC(menuPtr->display, menuPtr->textGC);
  114.     }
  115.     if (menuPtr->disabledImageGC != None) {
  116.     Tk_FreeGC(menuPtr->display, menuPtr->disabledImageGC);
  117.     }
  118.     if (menuPtr->gray != None) {
  119.     Tk_FreeBitmap(menuPtr->display, menuPtr->gray);
  120.     }
  121.     if (menuPtr->disabledGC != None) {
  122.     Tk_FreeGC(menuPtr->display, menuPtr->disabledGC);
  123.     }
  124.     if (menuPtr->activeGC != None) {
  125.     Tk_FreeGC(menuPtr->display, menuPtr->activeGC);
  126.     }
  127.     if (menuPtr->indicatorGC != None) {
  128.     Tk_FreeGC(menuPtr->display, menuPtr->indicatorGC);
  129.     }
  130. }
  131.  
  132. /*
  133.  *----------------------------------------------------------------------
  134.  *
  135.  * TkMenuEntryFreeDrawOptions --
  136.  *
  137.  *    Frees up drawing structures for a menu entry. Called when
  138.  *    menu entry is freed.
  139.  *
  140.  * RESULTS:
  141.  *    None.
  142.  *
  143.  * Side effects:
  144.  *    Storage is freed.
  145.  *
  146.  *----------------------------------------------------------------------
  147.  */
  148.  
  149. void
  150. TkMenuEntryFreeDrawOptions(mePtr)
  151.     TkMenuEntry *mePtr;
  152. {
  153.     if (mePtr->textGC != None) {
  154.     Tk_FreeGC(mePtr->menuPtr->display, mePtr->textGC);
  155.     }
  156.     if (mePtr->disabledGC != None) {
  157.     Tk_FreeGC(mePtr->menuPtr->display, mePtr->disabledGC);
  158.     }
  159.     if (mePtr->activeGC != None) {
  160.     Tk_FreeGC(mePtr->menuPtr->display, mePtr->activeGC);
  161.     }
  162.     if (mePtr->indicatorGC != None) {
  163.     Tk_FreeGC(mePtr->menuPtr->display, mePtr->indicatorGC);
  164.     }
  165. }
  166.  
  167. /*
  168.  *----------------------------------------------------------------------
  169.  *
  170.  * TkMenuConfigureDrawOptions --
  171.  *
  172.  *    Sets the menu's drawing attributes in preparation for drawing
  173.  *    the menu.
  174.  *
  175.  * RESULTS:
  176.  *    None.
  177.  *
  178.  * Side effects:
  179.  *    Storage is allocated.
  180.  *
  181.  *----------------------------------------------------------------------
  182.  */
  183.  
  184. void
  185. TkMenuConfigureDrawOptions(menuPtr)
  186.     TkMenu *menuPtr;        /* The menu we are configuring. */
  187. {
  188.     XGCValues gcValues;
  189.     GC newGC;
  190.     unsigned long mask;
  191.  
  192.     /*
  193.      * A few options need special processing, such as setting the
  194.      * background from a 3-D border, or filling in complicated
  195.      * defaults that couldn't be specified to Tk_ConfigureWidget.
  196.      */
  197.  
  198.     Tk_SetBackgroundFromBorder(menuPtr->tkwin, menuPtr->border);
  199.  
  200.     gcValues.font = Tk_FontId(menuPtr->tkfont);
  201.     gcValues.foreground = menuPtr->fg->pixel;
  202.     gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel;
  203.     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
  204.         &gcValues);
  205.     if (menuPtr->textGC != None) {
  206.     Tk_FreeGC(menuPtr->display, menuPtr->textGC);
  207.     }
  208.     menuPtr->textGC = newGC;
  209.  
  210.     gcValues.font = Tk_FontId(menuPtr->tkfont);
  211.     gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel;
  212.     if (menuPtr->disabledFg != NULL) {
  213.     gcValues.foreground = menuPtr->disabledFg->pixel;
  214.     mask = GCForeground|GCBackground|GCFont;
  215.     } else {
  216.     gcValues.foreground = gcValues.background;
  217.     mask = GCForeground;
  218.     if (menuPtr->gray == None) {
  219.         menuPtr->gray = Tk_GetBitmap(menuPtr->interp, menuPtr->tkwin,
  220.             Tk_GetUid("gray50"));
  221.     }
  222.     if (menuPtr->gray != None) {
  223.         gcValues.fill_style = FillStippled;
  224.         gcValues.stipple = menuPtr->gray;
  225.         mask = GCForeground|GCFillStyle|GCStipple;
  226.     }
  227.     }
  228.     newGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
  229.     if (menuPtr->disabledGC != None) {
  230.     Tk_FreeGC(menuPtr->display, menuPtr->disabledGC);
  231.     }
  232.     menuPtr->disabledGC = newGC;
  233.  
  234.     gcValues.foreground = Tk_3DBorderColor(menuPtr->border)->pixel;
  235.     if (menuPtr->gray == None) {
  236.     menuPtr->gray = Tk_GetBitmap(menuPtr->interp, menuPtr->tkwin,
  237.         Tk_GetUid("gray50"));
  238.     }
  239.     if (menuPtr->gray != None) {
  240.     gcValues.fill_style = FillStippled;
  241.     gcValues.stipple = menuPtr->gray;
  242.     newGC = Tk_GetGC(menuPtr->tkwin, 
  243.         GCForeground|GCFillStyle|GCStipple, &gcValues);
  244.     }
  245.     if (menuPtr->disabledImageGC != None) {
  246.     Tk_FreeGC(menuPtr->display, menuPtr->disabledImageGC);
  247.     }
  248.     menuPtr->disabledImageGC = newGC;
  249.  
  250.     gcValues.font = Tk_FontId(menuPtr->tkfont);
  251.     gcValues.foreground = menuPtr->activeFg->pixel;
  252.     gcValues.background =
  253.         Tk_3DBorderColor(menuPtr->activeBorder)->pixel;
  254.     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
  255.         &gcValues);
  256.     if (menuPtr->activeGC != None) {
  257.     Tk_FreeGC(menuPtr->display, menuPtr->activeGC);
  258.     }
  259.     menuPtr->activeGC = newGC;
  260.  
  261.     gcValues.foreground = menuPtr->indicatorFg->pixel;
  262.     gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel;
  263.     newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
  264.         &gcValues);
  265.     if (menuPtr->indicatorGC != None) {
  266.     Tk_FreeGC(menuPtr->display, menuPtr->indicatorGC);
  267.     }
  268.     menuPtr->indicatorGC = newGC;
  269. }
  270.  
  271. /*
  272.  *----------------------------------------------------------------------
  273.  *
  274.  * TkMenuConfigureEntryDrawOptions --
  275.  *
  276.  *    Calculates any entry-specific draw options for the given menu
  277.  *    entry.
  278.  *
  279.  * Results:
  280.  *    Returns a standard Tcl error.
  281.  *
  282.  * Side effects:
  283.  *    Storage may be allocated.
  284.  *
  285.  *----------------------------------------------------------------------
  286.  */
  287.  
  288. int
  289. TkMenuConfigureEntryDrawOptions(mePtr, index)
  290.     TkMenuEntry *mePtr;
  291.     int index;
  292. {
  293.  
  294.     XGCValues gcValues;
  295.     GC newGC, newActiveGC, newDisabledGC, newIndicatorGC;
  296.     unsigned long mask;
  297.     Tk_Font tkfont;
  298.     TkMenu *menuPtr = mePtr->menuPtr;
  299.  
  300.     tkfont = (mePtr->tkfont == NULL) ? menuPtr->tkfont : mePtr->tkfont;
  301.     
  302.     if (mePtr->state == tkActiveUid) {
  303.     if (index != menuPtr->active) {
  304.         TkActivateMenuEntry(menuPtr, index);
  305.     }
  306.     } else {
  307.     if (index == menuPtr->active) {
  308.         TkActivateMenuEntry(menuPtr, -1);
  309.     }
  310.     if ((mePtr->state != tkNormalUid)
  311.         && (mePtr->state != tkDisabledUid)) {
  312.         Tcl_AppendResult(menuPtr->interp, "bad state value \"",
  313.             mePtr->state,
  314.             "\": must be normal, active, or disabled", (char *) NULL);
  315.         mePtr->state = tkNormalUid;
  316.         return TCL_ERROR;
  317.     }
  318.     }
  319.  
  320.     if ((mePtr->tkfont != NULL)
  321.         || (mePtr->border != NULL)
  322.         || (mePtr->fg != NULL)
  323.         || (mePtr->activeBorder != NULL)
  324.         || (mePtr->activeFg != NULL)
  325.         || (mePtr->indicatorFg != NULL)) {
  326.     gcValues.foreground = (mePtr->fg != NULL)
  327.             ? mePtr->fg->pixel
  328.         : menuPtr->fg->pixel;
  329.     gcValues.background = Tk_3DBorderColor(
  330.         (mePtr->border != NULL)
  331.         ? mePtr->border
  332.         : menuPtr->border)
  333.         ->pixel;
  334.  
  335.     gcValues.font = Tk_FontId(tkfont);
  336.  
  337.     /*
  338.      * Note: disable GraphicsExpose events;  we know there won't be
  339.      * obscured areas when copying from an off-screen pixmap to the
  340.      * screen and this gets rid of unnecessary events.
  341.      */
  342.  
  343.     gcValues.graphics_exposures = False;
  344.     newGC = Tk_GetGC(menuPtr->tkwin,
  345.         GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  346.         &gcValues);
  347.  
  348.     if (mePtr->indicatorFg != NULL) {
  349.         gcValues.foreground = mePtr->indicatorFg->pixel;
  350.     } else if (menuPtr->indicatorFg != NULL) {
  351.         gcValues.foreground = menuPtr->indicatorFg->pixel;
  352.     }
  353.     newIndicatorGC = Tk_GetGC(menuPtr->tkwin,
  354.         GCForeground|GCBackground|GCGraphicsExposures,
  355.         &gcValues);
  356.  
  357.     if ((menuPtr->disabledFg != NULL) || (mePtr->image != NULL)) {
  358.         gcValues.foreground = menuPtr->disabledFg->pixel;
  359.         mask = GCForeground|GCBackground|GCFont|GCGraphicsExposures;
  360.     } else {
  361.         gcValues.foreground = gcValues.background;
  362.         gcValues.fill_style = FillStippled;
  363.         gcValues.stipple = menuPtr->gray;
  364.         mask = GCForeground|GCFillStyle|GCStipple;
  365.     }
  366.     newDisabledGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
  367.  
  368.     gcValues.foreground = (mePtr->activeFg != NULL)
  369.         ? mePtr->activeFg->pixel
  370.             : menuPtr->activeFg->pixel;
  371.     gcValues.background = Tk_3DBorderColor(
  372.         (mePtr->activeBorder != NULL)
  373.         ? mePtr->activeBorder
  374.         : menuPtr->activeBorder)->pixel;
  375.     newActiveGC = Tk_GetGC(menuPtr->tkwin,
  376.         GCForeground|GCBackground|GCFont|GCGraphicsExposures,
  377.         &gcValues);
  378.     } else {
  379.     newGC = None;
  380.     newActiveGC = None;
  381.     newDisabledGC = None;
  382.     newIndicatorGC = None;
  383.     }
  384.     if (mePtr->textGC != None) {
  385.         Tk_FreeGC(menuPtr->display, mePtr->textGC);
  386.     }
  387.     mePtr->textGC = newGC;
  388.     if (mePtr->activeGC != None) {
  389.         Tk_FreeGC(menuPtr->display, mePtr->activeGC);
  390.     }
  391.     mePtr->activeGC = newActiveGC;
  392.     if (mePtr->disabledGC != None) {
  393.         Tk_FreeGC(menuPtr->display, mePtr->disabledGC);
  394.     }
  395.     mePtr->disabledGC = newDisabledGC;
  396.     if (mePtr->indicatorGC != None) {
  397.     Tk_FreeGC(menuPtr->display, mePtr->indicatorGC);
  398.     }
  399.     mePtr->indicatorGC = newIndicatorGC;
  400.     return TCL_OK;
  401. }
  402.  
  403. /*
  404.  *----------------------------------------------------------------------
  405.  *
  406.  * TkEventuallyRecomputeMenu --
  407.  *
  408.  *    Tells Tcl to redo the geometry because this menu has changed.
  409.  *
  410.  * Results:
  411.  *    None.
  412.  *
  413.  * Side effects:
  414.  *    Menu geometry is recomputed at idle time, and the menu will be
  415.  *    redisplayed.
  416.  *
  417.  *----------------------------------------------------------------------
  418.  */
  419.  
  420. void
  421. TkEventuallyRecomputeMenu(menuPtr)
  422.     TkMenu *menuPtr;
  423. {
  424.     if (!(menuPtr->menuFlags & RESIZE_PENDING)) {
  425.     menuPtr->menuFlags |= RESIZE_PENDING;
  426.     Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
  427.     }
  428. }
  429.  
  430. /*
  431.  *----------------------------------------------------------------------
  432.  *
  433.  * TkRecomputeMenu --
  434.  *
  435.  *    Tells Tcl to redo the geometry because this menu has changed.
  436.  *    Does it now; removes any ComputeMenuGeometries from the idler.
  437.  *
  438.  * Results:
  439.  *    None.
  440.  *
  441.  * Side effects:
  442.  *    Menu geometry is immediately reconfigured.
  443.  *
  444.  *----------------------------------------------------------------------
  445.  */
  446.  
  447. void
  448. TkRecomputeMenu(menuPtr)
  449.     TkMenu *menuPtr;
  450. {    
  451.     if (menuPtr->menuFlags & RESIZE_PENDING) {
  452.     Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
  453.     ComputeMenuGeometry((ClientData) menuPtr);
  454.     }
  455. }
  456.  
  457. /*
  458.  *----------------------------------------------------------------------
  459.  *
  460.  * TkEventuallyRedrawMenu --
  461.  *
  462.  *    Arrange for an entry of a menu, or the whole menu, to be
  463.  *    redisplayed at some point in the future.
  464.  *
  465.  * Results:
  466.  *    None.
  467.  *
  468.  * Side effects:
  469.  *    A when-idle hander is scheduled to do the redisplay, if there
  470.  *    isn't one already scheduled.
  471.  *
  472.  *----------------------------------------------------------------------
  473.  */
  474.  
  475. void
  476. TkEventuallyRedrawMenu(menuPtr, mePtr)
  477.     register TkMenu *menuPtr;    /* Information about menu to redraw. */
  478.     register TkMenuEntry *mePtr;    /* Entry to redraw.  NULL means redraw
  479.                  * all the entries in the menu. */
  480. {
  481.     int i;
  482.     
  483.     if (menuPtr->tkwin == NULL) {
  484.     return;
  485.     }
  486.     if (mePtr != NULL) {
  487.     mePtr->entryFlags |= ENTRY_NEEDS_REDISPLAY;
  488.     } else {
  489.     for (i = 0; i < menuPtr->numEntries; i++) {
  490.         menuPtr->entries[i]->entryFlags |= ENTRY_NEEDS_REDISPLAY;
  491.     }
  492.     }
  493.     if (!Tk_IsMapped(menuPtr->tkwin)
  494.         || (menuPtr->menuFlags & REDRAW_PENDING)) {
  495.     return;
  496.     }
  497.     Tcl_DoWhenIdle(DisplayMenu, (ClientData) menuPtr);
  498.     menuPtr->menuFlags |= REDRAW_PENDING;
  499. }
  500.  
  501. /*
  502.  *--------------------------------------------------------------
  503.  *
  504.  * ComputeMenuGeometry --
  505.  *
  506.  *    This procedure is invoked to recompute the size and
  507.  *    layout of a menu.  It is called as a when-idle handler so
  508.  *    that it only gets done once, even if a group of changes is
  509.  *    made to the menu.
  510.  *
  511.  * Results:
  512.  *    None.
  513.  *
  514.  * Side effects:
  515.  *    Fields of menu entries are changed to reflect their
  516.  *    current positions, and the size of the menu window
  517.  *    itself may be changed.
  518.  *
  519.  *--------------------------------------------------------------
  520.  */
  521.  
  522. static void
  523. ComputeMenuGeometry(clientData)
  524.     ClientData clientData;        /* Structure describing menu. */
  525. {
  526.     TkMenu *menuPtr = (TkMenu *) clientData;
  527.  
  528.     if (menuPtr->tkwin == NULL) {
  529.     return;
  530.     }
  531.  
  532.     if (menuPtr->menuType == MENUBAR) {
  533.     TkpComputeMenubarGeometry(menuPtr);
  534.     } else {
  535.     TkpComputeStandardMenuGeometry(menuPtr);
  536.     }
  537.  
  538.     if ((menuPtr->totalWidth != Tk_ReqWidth(menuPtr->tkwin)) ||
  539.         (menuPtr->totalHeight != Tk_ReqHeight(menuPtr->tkwin))) {
  540.     Tk_GeometryRequest(menuPtr->tkwin, menuPtr->totalWidth,
  541.         menuPtr->totalHeight);
  542.     }
  543.     
  544.     /*
  545.      * Must always force a redisplay here if the window is mapped
  546.      * (even if the size didn't change, something else might have
  547.      * changed in the menu, such as a label or accelerator).  The
  548.      * resize will force a redisplay above.
  549.      */
  550.     
  551.     TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  552.     
  553.     menuPtr->menuFlags &= ~RESIZE_PENDING;
  554. }
  555.  
  556. /*
  557.  *----------------------------------------------------------------------
  558.  *
  559.  * TkMenuSelectImageProc --
  560.  *
  561.  *    This procedure is invoked by the image code whenever the manager
  562.  *    for an image does something that affects the size of contents
  563.  *    of an image displayed in a menu entry when it is selected.
  564.  *
  565.  * Results:
  566.  *    None.
  567.  *
  568.  * Side effects:
  569.  *    Arranges for the menu to get redisplayed.
  570.  *
  571.  *----------------------------------------------------------------------
  572.  */
  573.  
  574. void
  575. TkMenuSelectImageProc(clientData, x, y, width, height, imgWidth,
  576.     imgHeight)
  577.     ClientData clientData;        /* Pointer to widget record. */
  578.     int x, y;                /* Upper left pixel (within image)
  579.                      * that must be redisplayed. */
  580.     int width, height;            /* Dimensions of area to redisplay
  581.                      * (may be <= 0). */
  582.     int imgWidth, imgHeight;        /* New dimensions of image. */
  583. {
  584.     register TkMenuEntry *mePtr = (TkMenuEntry *) clientData;
  585.  
  586.     if ((mePtr->entryFlags & ENTRY_SELECTED)
  587.         && !(mePtr->menuPtr->menuFlags &
  588.         REDRAW_PENDING)) {
  589.     mePtr->menuPtr->menuFlags |= REDRAW_PENDING;
  590.     Tcl_DoWhenIdle(DisplayMenu, (ClientData) mePtr->menuPtr);
  591.     }
  592. }
  593.  
  594. /*
  595.  *----------------------------------------------------------------------
  596.  *
  597.  * DisplayMenu --
  598.  *
  599.  *    This procedure is invoked to display a menu widget.
  600.  *
  601.  * Results:
  602.  *    None.
  603.  *
  604.  * Side effects:
  605.  *    Commands are output to X to display the menu in its
  606.  *    current mode.
  607.  *
  608.  *----------------------------------------------------------------------
  609.  */
  610.  
  611. static void
  612. DisplayMenu(clientData)
  613.     ClientData clientData;    /* Information about widget. */
  614. {
  615.     register TkMenu *menuPtr = (TkMenu *) clientData;
  616.     register TkMenuEntry *mePtr;
  617.     register Tk_Window tkwin = menuPtr->tkwin;
  618.     int index, strictMotif;
  619.     Tk_Font tkfont = menuPtr->tkfont;
  620.     Tk_FontMetrics menuMetrics;
  621.     int width;
  622.  
  623.     menuPtr->menuFlags &= ~REDRAW_PENDING;
  624.     if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  625.     return;
  626.     }
  627.  
  628.     if (menuPtr->menuType == MENUBAR) {
  629.     Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border,
  630.         menuPtr->borderWidth, menuPtr->borderWidth,
  631.         Tk_Width(tkwin) - 2 * menuPtr->borderWidth,
  632.         Tk_Height(tkwin) - 2 * menuPtr->borderWidth, 0,
  633.         TK_RELIEF_FLAT);
  634.     }
  635.  
  636.     strictMotif = Tk_StrictMotif(menuPtr->tkwin);
  637.  
  638.     /*
  639.      * See note in ComputeMenuGeometry. We don't want to be doing font metrics
  640.      * all of the time.
  641.      */
  642.  
  643.     Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics);
  644.  
  645.     /*
  646.      * Loop through all of the entries, drawing them one at a time.
  647.      */
  648.  
  649.     for (index = 0; index < menuPtr->numEntries; index++) {
  650.     mePtr = menuPtr->entries[index];
  651.     if (menuPtr->menuType != MENUBAR) {
  652.         if (!(mePtr->entryFlags & ENTRY_NEEDS_REDISPLAY)) {
  653.         continue;
  654.         }
  655.     }
  656.     mePtr->entryFlags &= ~ENTRY_NEEDS_REDISPLAY;
  657.  
  658.     if (menuPtr->menuType == MENUBAR) {
  659.         width = mePtr->width;
  660.     } else {
  661.         if (mePtr->entryFlags & ENTRY_LAST_COLUMN) {
  662.         width = Tk_Width(menuPtr->tkwin) - mePtr->x
  663.             - menuPtr->activeBorderWidth;
  664.         } else {
  665.         width = mePtr->width + menuPtr->borderWidth;
  666.         }
  667.     }
  668.     TkpDrawMenuEntry(mePtr, Tk_WindowId(menuPtr->tkwin), tkfont,
  669.         &menuMetrics, mePtr->x, mePtr->y, width, 
  670.         mePtr->height, strictMotif, 1);
  671.     if ((index > 0) && (menuPtr->menuType != MENUBAR) 
  672.         && mePtr->columnBreak) {
  673.         mePtr = menuPtr->entries[index - 1];
  674.         Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border,
  675.         mePtr->x, mePtr->y + mePtr->height, 
  676.         mePtr->width,
  677.         Tk_Height(tkwin) - mePtr->y - mePtr->height 
  678.         - menuPtr->activeBorderWidth, 0,
  679.         TK_RELIEF_FLAT);
  680.     }
  681.     }
  682.  
  683.     if (menuPtr->menuType != MENUBAR) {
  684.     int x, y, height;
  685.  
  686.     if (menuPtr->numEntries == 0) {
  687.         x = y = menuPtr->borderWidth;
  688.         width = Tk_Width(tkwin) - 2 * menuPtr->activeBorderWidth;
  689.         height = Tk_Height(tkwin) - 2 * menuPtr->activeBorderWidth;
  690.     } else {
  691.         mePtr = menuPtr->entries[menuPtr->numEntries - 1];
  692.         Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
  693.         menuPtr->border, mePtr->x, mePtr->y + mePtr->height,
  694.         mePtr->width, Tk_Height(tkwin) - mePtr->y - mePtr->height
  695.         - menuPtr->activeBorderWidth, 0,
  696.         TK_RELIEF_FLAT);
  697.         x = mePtr->x + mePtr->width;
  698.         y = mePtr->y + mePtr->height;
  699.         width = Tk_Width(tkwin) - x - menuPtr->activeBorderWidth;
  700.         height = Tk_Height(tkwin) - y - menuPtr->activeBorderWidth;
  701.     }
  702.     Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), menuPtr->border, x, y, 
  703.         width, height, 0, TK_RELIEF_FLAT);
  704.     }
  705.  
  706.     Tk_Draw3DRectangle(menuPtr->tkwin, Tk_WindowId(tkwin),
  707.         menuPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  708.         menuPtr->borderWidth, menuPtr->relief);
  709. }
  710.  
  711. /*
  712.  *--------------------------------------------------------------
  713.  *
  714.  * TkMenuEventProc --
  715.  *
  716.  *    This procedure is invoked by the Tk dispatcher for various
  717.  *    events on menus.
  718.  *
  719.  * Results:
  720.  *    None.
  721.  *
  722.  * Side effects:
  723.  *    When the window gets deleted, internal structures get
  724.  *    cleaned up.  When it gets exposed, it is redisplayed.
  725.  *
  726.  *--------------------------------------------------------------
  727.  */
  728.  
  729. void
  730. TkMenuEventProc(clientData, eventPtr)
  731.     ClientData clientData;    /* Information about window. */
  732.     XEvent *eventPtr;        /* Information about event. */
  733. {
  734.     TkMenu *menuPtr = (TkMenu *) clientData;
  735.     
  736.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  737.     TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  738.     } else if (eventPtr->type == ConfigureNotify) {
  739.     TkEventuallyRecomputeMenu(menuPtr);
  740.     TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  741.     } else if (eventPtr->type == ActivateNotify) {
  742.         TkpSetMainMenubar(menuPtr->interp, menuPtr->tkwin, NULL);
  743.     } else if (eventPtr->type == DestroyNotify) {
  744.     if (menuPtr->tkwin != NULL) {
  745.         menuPtr->tkwin = NULL;
  746.         Tcl_DeleteCommandFromToken(menuPtr->interp, menuPtr->widgetCmd);
  747.     }
  748.     if (menuPtr->menuFlags & REDRAW_PENDING) {
  749.         Tcl_CancelIdleCall(DisplayMenu, (ClientData) menuPtr);
  750.     }
  751.     if (menuPtr->menuFlags & RESIZE_PENDING) {
  752.         Tcl_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
  753.     }
  754.     TkDestroyMenu(menuPtr);
  755.     }
  756. }
  757.  
  758. /*
  759.  *----------------------------------------------------------------------
  760.  *
  761.  * TkMenuImageProc --
  762.  *
  763.  *    This procedure is invoked by the image code whenever the manager
  764.  *    for an image does something that affects the size of contents
  765.  *    of an image displayed in a menu entry.
  766.  *
  767.  * Results:
  768.  *    None.
  769.  *
  770.  * Side effects:
  771.  *    Arranges for the menu to get redisplayed.
  772.  *
  773.  *----------------------------------------------------------------------
  774.  */
  775.  
  776. void
  777. TkMenuImageProc(clientData, x, y, width, height, imgWidth,
  778.     imgHeight)
  779.     ClientData clientData;        /* Pointer to widget record. */
  780.     int x, y;                /* Upper left pixel (within image)
  781.                      * that must be redisplayed. */
  782.     int width, height;            /* Dimensions of area to redisplay
  783.                      * (may be <= 0). */
  784.     int imgWidth, imgHeight;        /* New dimensions of image. */
  785. {
  786.     register TkMenu *menuPtr = ((TkMenuEntry *)clientData)->menuPtr;
  787.  
  788.     if ((menuPtr->tkwin != NULL) && !(menuPtr->menuFlags
  789.         & RESIZE_PENDING)) {
  790.     menuPtr->menuFlags |= RESIZE_PENDING;
  791.     Tcl_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
  792.     }
  793. }
  794.  
  795. /*
  796.  *----------------------------------------------------------------------
  797.  *
  798.  * TkPostTearoffMenu --
  799.  *
  800.  *    Posts a menu on the screen. Used to post tearoff menus. On Unix,
  801.  *    all menus are posted this way. Adjusts the menu's position
  802.  *    so that it fits on the screen, and maps and raises the menu.
  803.  *
  804.  * Results:
  805.  *    Returns a standard Tcl Error.
  806.  *
  807.  * Side effects:
  808.  *    The menu is posted.
  809.  *
  810.  *----------------------------------------------------------------------
  811.  */
  812.  
  813. int
  814. TkPostTearoffMenu(interp, menuPtr, x, y)
  815.     Tcl_Interp *interp;            /* The interpreter of the menu */
  816.     TkMenu *menuPtr;            /* The menu we are posting */
  817.     int x;                /* The root X coordinate where we
  818.                      * are posting */
  819.     int y;                /* The root Y coordinate where we
  820.                      * are posting */
  821. {
  822.     int vRootX, vRootY, vRootWidth, vRootHeight;
  823.     int tmp, result;
  824.  
  825.     TkActivateMenuEntry(menuPtr, -1);
  826.     TkRecomputeMenu(menuPtr);
  827.     result = TkPostCommand(menuPtr);
  828.     if (result != TCL_OK) {
  829.         return result;
  830.     }
  831.  
  832.     /*
  833.      * The post commands could have deleted the menu, which means
  834.      * we are dead and should go away.
  835.      */
  836.  
  837.     if (menuPtr->tkwin == NULL) {
  838.         return TCL_OK;
  839.     }
  840.  
  841.     /*
  842.      * Adjust the position of the menu if necessary to keep it
  843.      * visible on the screen.  There are two special tricks to
  844.      * make this work right:
  845.      *
  846.      * 1. If a virtual root window manager is being used then
  847.      *    the coordinates are in the virtual root window of
  848.      *    menuPtr's parent;  since the menu uses override-redirect
  849.      *    mode it will be in the *real* root window for the screen,
  850.      *    so we have to map the coordinates from the virtual root
  851.      *    (if any) to the real root.  Can't get the virtual root
  852.      *    from the menu itself (it will never be seen by the wm)
  853.      *    so use its parent instead (it would be better to have an
  854.      *    an option that names a window to use for this...).
  855.      * 2. The menu may not have been mapped yet, so its current size
  856.      *    might be the default 1x1.  To compute how much space it
  857.      *    needs, use its requested size, not its actual size.
  858.      *
  859.      * Note that this code assumes square screen regions and all
  860.      * positive coordinates. This does not work on a Mac with
  861.      * multiple monitors. But then again, Tk has other problems
  862.      * with this.
  863.      */
  864.  
  865.     Tk_GetVRootGeometry(Tk_Parent(menuPtr->tkwin), &vRootX, &vRootY,
  866.     &vRootWidth, &vRootHeight);
  867.     x += vRootX;
  868.     y += vRootY;
  869.     tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin))
  870.     - Tk_ReqWidth(menuPtr->tkwin);
  871.     if (x > tmp) {
  872.     x = tmp;
  873.     }
  874.     if (x < 0) {
  875.     x = 0;
  876.     }
  877.     tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin))
  878.     - Tk_ReqHeight(menuPtr->tkwin);
  879.     if (y > tmp) {
  880.     y = tmp;
  881.     }
  882.     if (y < 0) {
  883.     y = 0;
  884.     }
  885.     Tk_MoveToplevelWindow(menuPtr->tkwin, x, y);
  886.     if (!Tk_IsMapped(menuPtr->tkwin)) {
  887.     Tk_MapWindow(menuPtr->tkwin);
  888.     }
  889.     TkWmRestackToplevel((TkWindow *) menuPtr->tkwin, Above, NULL);
  890.     return TCL_OK;
  891. }
  892.  
  893. /*
  894.  *--------------------------------------------------------------
  895.  *
  896.  * TkPostSubmenu --
  897.  *
  898.  *    This procedure arranges for a particular submenu (i.e. the
  899.  *    menu corresponding to a given cascade entry) to be
  900.  *    posted.
  901.  *
  902.  * Results:
  903.  *    A standard Tcl return result.  Errors may occur in the
  904.  *    Tcl commands generated to post and unpost submenus.
  905.  *
  906.  * Side effects:
  907.  *    If there is already a submenu posted, it is unposted.
  908.  *    The new submenu is then posted.
  909.  *
  910.  *--------------------------------------------------------------
  911.  */
  912.  
  913. int
  914. TkPostSubmenu(interp, menuPtr, mePtr)
  915.     Tcl_Interp *interp;        /* Used for invoking sub-commands and
  916.                  * reporting errors. */
  917.     register TkMenu *menuPtr;    /* Information about menu as a whole. */
  918.     register TkMenuEntry *mePtr;    /* Info about submenu that is to be
  919.                  * posted.  NULL means make sure that
  920.                  * no submenu is posted. */
  921. {
  922.     char string[30];
  923.     int result, x, y;
  924.  
  925.     if (mePtr == menuPtr->postedCascade) {
  926.     return TCL_OK;
  927.     }
  928.  
  929.     if (menuPtr->postedCascade != NULL) {
  930.  
  931.     /*
  932.      * Note: when unposting a submenu, we have to redraw the entire
  933.      * parent menu.  This is because of a combination of the following
  934.      * things:
  935.      * (a) the submenu partially overlaps the parent.
  936.      * (b) the submenu specifies "save under", which causes the X
  937.      *     server to make a copy of the information under it when it
  938.      *     is posted.  When the submenu is unposted, the X server
  939.      *     copies this data back and doesn't generate any Expose
  940.      *     events for the parent.
  941.      * (c) the parent may have redisplayed itself after the submenu
  942.      *     was posted, in which case the saved information is no
  943.      *     longer correct.
  944.      * The simplest solution is just force a complete redisplay of
  945.      * the parent.
  946.      */
  947.  
  948.     TkEventuallyRedrawMenu(menuPtr, (TkMenuEntry *) NULL);
  949.     result = Tcl_VarEval(interp, menuPtr->postedCascade->name,
  950.         " unpost", (char *) NULL);
  951.     menuPtr->postedCascade = NULL;
  952.     if (result != TCL_OK) {
  953.         return result;
  954.     }
  955.     }
  956.  
  957.     if ((mePtr != NULL) && (mePtr->name != NULL)
  958.         && Tk_IsMapped(menuPtr->tkwin)) {
  959.  
  960.     /*
  961.      * Position the cascade with its upper left corner slightly
  962.      * below and to the left of the upper right corner of the
  963.      * menu entry (this is an attempt to match Motif behavior).
  964.      *
  965.      * The menu has to redrawn so that the entry can change relief.
  966.      */
  967.  
  968.     Tk_GetRootCoords(menuPtr->tkwin, &x, &y);
  969.     AdjustMenuCoords(menuPtr, mePtr, &x, &y, string);
  970.     result = Tcl_VarEval(interp, mePtr->name, " post ", string,
  971.         (char *) NULL);
  972.     if (result != TCL_OK) {
  973.         return result;
  974.     }
  975.     menuPtr->postedCascade = mePtr;
  976.     TkEventuallyRedrawMenu(menuPtr, mePtr);
  977.     }
  978.     return TCL_OK;
  979. }
  980.  
  981. /*
  982.  *----------------------------------------------------------------------
  983.  *
  984.  * AdjustMenuCoords --
  985.  *
  986.  *    Adjusts the given coordinates down and the left to give a Motif
  987.  *    look.
  988.  *
  989.  * Results:
  990.  *    None.
  991.  *
  992.  * Side effects:
  993.  *    The menu is eventually redrawn if necessary.
  994.  *
  995.  *----------------------------------------------------------------------
  996.  */
  997.  
  998. static void
  999. AdjustMenuCoords(menuPtr, mePtr, xPtr, yPtr, string)
  1000.     TkMenu *menuPtr;
  1001.     TkMenuEntry *mePtr;
  1002.     int *xPtr;
  1003.     int *yPtr;
  1004.     char *string;
  1005. {
  1006.     if (menuPtr->menuType == MENUBAR) {
  1007.     *xPtr += mePtr->x;
  1008.     *yPtr += mePtr->y + mePtr->height;
  1009.     } else {
  1010.     *xPtr += Tk_Width(menuPtr->tkwin) - menuPtr->borderWidth
  1011.         - menuPtr->activeBorderWidth - 2;
  1012.     *yPtr += mePtr->y
  1013.             + menuPtr->activeBorderWidth + 2;
  1014.     }
  1015.     sprintf(string, "%d %d", *xPtr, *yPtr);
  1016. }
  1017.